<!DOCTYPE html>
<html lang="zh-Hans">

<!-- Head tag -->
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!--Description-->
    
        <meta name="description" content="Note">
    

    <!--Author-->
    
        <meta name="author" content="兰陵忧患生">
    

    <!-- Title -->
    
    <title>第一部分 Spring 的核心 | Lan</title>

    <!-- Bootstrap Core CSS -->
    <link href="//cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet"
      href="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.12.0/build/styles/default.min.css">
    <script src="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.12.0/build/highlight.min.js"></script>

    <!-- Custom CSS -->
    <link rel="stylesheet" href="/css/style.css">

    <!-- Custom Fonts -->
    <link href="//cdn.bootcss.com/font-awesome/4.5.0/css/font-awesome.min.css" rel="stylesheet" type="text/css">
    <link href='//fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
    <link href='//fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'>

    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
    <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
    <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
</head>

<body>

    <!-- Content -->
    <section class="article-container">
<!-- Back Home -->
<a class="nav-back" href="/">
    <i class="fa fa-puzzle-piece"></i>
</a>

<!-- Page Header -->
<header class="intro-header">
    <div class="container">
        <div class="row">
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
                <div class="post-heading">
                    <h1>第一部分 Spring 的核心</h1>
                </div>
            </div>
        </div>
    </div>
</header>

<!-- Post Content -->
<article>
    <div class="container">
        <div class="row">
            <!-- TOC -->
            <div class="toc-article col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
    <ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#高级装配"><span class="toc-text">高级装配</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#环境与-profile"><span class="toc-text">环境与 profile</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#配置-profile-bean"><span class="toc-text">配置 profile bean</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#激活-profile"><span class="toc-text">激活 profile</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#使用-profile进行测试"><span class="toc-text">使用 profile进行测试</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#条件化的-bean"><span class="toc-text">条件化的 bean</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#处理自动装配的歧义性"><span class="toc-text">处理自动装配的歧义性</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#标示首选的-bean"><span class="toc-text">标示首选的 bean</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#限定自动装配的-bean"><span class="toc-text">限定自动装配的 bean</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#创建自定义的限定符"><span class="toc-text">创建自定义的限定符</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#使用自定义的限定符注解"><span class="toc-text">使用自定义的限定符注解</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#bean-的作用域"><span class="toc-text">bean 的作用域</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#使用会话和请求作用域"><span class="toc-text">使用会话和请求作用域</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#在-XML-中声明作用域代理"><span class="toc-text">在 XML 中声明作用域代理</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#运行时值注入"><span class="toc-text">运行时值注入</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#注入外部的值"><span class="toc-text">注入外部的值</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#深入学习-Spring-的-Environment"><span class="toc-text">深入学习 Spring 的 Environment</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#解析属性占位符"><span class="toc-text">解析属性占位符</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#使用-Spring-表达式语言进行装配"><span class="toc-text">使用 Spring 表达式语言进行装配</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#表示字面值"><span class="toc-text">表示字面值</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#引用-bean，属性和方法"><span class="toc-text">引用 bean，属性和方法</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#在表达式中使用类型"><span class="toc-text">在表达式中使用类型</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#SpEL-运算符"><span class="toc-text">SpEL 运算符</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#计算正则表达式"><span class="toc-text">计算正则表达式</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#计算集合"><span class="toc-text">计算集合</span></a></li></ol></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#面向切面的-Spring"><span class="toc-text">面向切面的 Spring</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#什么是面向切面编程"><span class="toc-text">什么是面向切面编程</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#定义-AOP-术语"><span class="toc-text">定义 AOP 术语</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Spring-对-AOP-的支持"><span class="toc-text">Spring 对 AOP 的支持</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#Spring-通知是-Java-编写的"><span class="toc-text">Spring 通知是 Java 编写的</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Spring-在运行时通知对象"><span class="toc-text">Spring 在运行时通知对象</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Spring-只支持方法级别的连接点"><span class="toc-text">Spring 只支持方法级别的连接点</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#通过切点来选择连接点"><span class="toc-text">通过切点来选择连接点</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#编写切点"><span class="toc-text">编写切点</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#在切点中选择-bean"><span class="toc-text">在切点中选择 bean</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#使用注解创建切面"><span class="toc-text">使用注解创建切面</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#定义切面"><span class="toc-text">定义切面</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#创建环绕通知"><span class="toc-text">创建环绕通知</span></a></li></ol></li></ol></li></ol>
</div>

            <!-- Post Main Content -->
            <div class="post-content col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
                <h1 id="高级装配"><a href="#高级装配" class="headerlink" title="高级装配"></a>高级装配</h1><h2 id="环境与-profile"><a href="#环境与-profile" class="headerlink" title="环境与 profile"></a>环境与 profile</h2><h3 id="配置-profile-bean"><a href="#配置-profile-bean" class="headerlink" title="配置 profile bean"></a>配置 profile bean</h3><ul>
<li><code>@Profile(&quot;dev&quot;)</code></li>
<li><code>&lt;beans profile=&quot;dev&quot;&gt;&lt;/beans&gt;</code></li>
</ul>
<h3 id="激活-profile"><a href="#激活-profile" class="headerlink" title="激活 profile"></a>激活 profile</h3><ul>
<li>spring.profiles.active</li>
<li>spring.profiles.default</li>
</ul>
<h4 id="使用-profile进行测试"><a href="#使用-profile进行测试" class="headerlink" title="使用 profile进行测试"></a>使用 profile进行测试</h4><ul>
<li><code>@ActiveProfiles(&quot;dev&quot;)</code></li>
</ul>
<h2 id="条件化的-bean"><a href="#条件化的-bean" class="headerlink" title="条件化的 bean"></a>条件化的 bean</h2><ul>
<li>@Conditional(MagicExistsCondition.class)</li>
</ul>
<pre><code class="Java">public interface Condition {
    boolean matches(ConditionContext ctxt, AnnotatedTypeMetadata metadata);
}

public interface ConditionContext {
    BeanDefinitionRegistry getRegistry();
    ConfigurableListableBeanFactory getBeanFactory();
    Environment getEnvironment();
    ResourceLoader getResourceLoader();
    ClassLoader getClassLoader();
}

public interface AnnotatedTypeMetadata {
    boolean isAnnotated(String annotationType);
    Map&lt;String, Object&gt; getAnnotationAttributes(String annotationType);
    Map&lt;String, Object&gt; getAnnotationAttributes(String annotationType, boolean classValuesAsString);
    MultiValueMap&lt;String, Object&gt; getAllAnnotationAttributes(String annotationTyper);
    MultiValueMap&lt;String, Object&gt; getAllAnnotationAttributes(String annotationTyper, boolean classValuesAsString);
}
</code></pre>
<ul>
<li>Spring4开始，@Profile基于@Conditional和Condition实现</li>
</ul>
<pre><code class="Java">@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional(ProfileCondition.class) {
    String[] value();
}
</code></pre>
<h2 id="处理自动装配的歧义性"><a href="#处理自动装配的歧义性" class="headerlink" title="处理自动装配的歧义性"></a>处理自动装配的歧义性</h2><h3 id="标示首选的-bean"><a href="#标示首选的-bean" class="headerlink" title="标示首选的 bean"></a>标示首选的 bean</h3><ul>
<li><code>@Primary</code></li>
</ul>
<pre><code class="Java">@Component
@Primary
public class IceCream implements Dessert {...}

//Java显示配置
@Bean
@Primary
public Dessert iceCream {
    return new IceCream();
}

// XML
&lt;bean id=&quot;iceCream&quot; class=&quot;com.desserteater.IceCream&quot; primary=&quot;true&quot; /&gt;
</code></pre>
<h3 id="限定自动装配的-bean"><a href="#限定自动装配的-bean" class="headerlink" title="限定自动装配的 bean"></a>限定自动装配的 bean</h3><ul>
<li><code>@Qualifier(&quot;iceCream&quot;)</code></li>
</ul>
<pre><code class="Java">@Autowired
@Qualifier(&quot;iceCream&quot;)
public void setDessert(Dessert dessert) {
    this.dessert = dessert;
}
</code></pre>
<h4 id="创建自定义的限定符"><a href="#创建自定义的限定符" class="headerlink" title="创建自定义的限定符"></a>创建自定义的限定符</h4><pre><code class="Java">@Component
@Qualifier(&quot;cold&quot;)
public class IceCream implements Dessert {...}

@Autowired
@Qualifier(&quot;cold&quot;)
public void setDessert(Dessert dessert) {
    this.dessert = dessert;
}

// Java配置注解
@Bean
@Qualifier(&quot;cold&quot;)
public Dessert iceCream() {
    return new IceCream();
}
</code></pre>
<h4 id="使用自定义的限定符注解"><a href="#使用自定义的限定符注解" class="headerlink" title="使用自定义的限定符注解"></a>使用自定义的限定符注解</h4><ul>
<li>由于Java不允许在同一个条目上重复出现的多个注解。Java8可以，只要这个注解本身在定义时带有@Repeatable注解就可以。</li>
</ul>
<pre><code class="Java">@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, Element.METHOD, ElementType.TYPE)})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Cold { }

@Component
@Cold
@Creamy
public class IceCream implements Dessert {...}

// 注入点
@Autowired
@Cold
@Creamy
public void setDessert(Dessert dessert) {
    this.dessert = dessert;
}
</code></pre>
<ul>
<li>相对于使用原始的@Qualifier并借助String类型来指定限定符，自定义的注解也更为类型安全。</li>
</ul>
<h2 id="bean-的作用域"><a href="#bean-的作用域" class="headerlink" title="bean 的作用域"></a>bean 的作用域</h2><ul>
<li><p>Spring作用域</p>
<ul>
<li>单例(Singleton): 在整个应用中，只创建一个bean的一个实例(默认)</li>
<li>原型(Prototype): 每次注入或通过Spring应用上下文获取的时候，都会创建一个新的bean实例。</li>
<li>会话(Session): 在Web应用中，为每个会话创建一个bean实例</li>
<li>请求(Request): 在Web应用中，为每个请求创建一个bean实例</li>
</ul>
</li>
<li><p><code>@Scope</code>注解作用域</p>
</li>
</ul>
<pre><code class="Java">@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Notepad {...}

// 或使用@Scope(&quot;prototype&quot;)
// 但是使用SCOPE_PROTOTYPE常量更加安全并且不易出错

@Bean
@Scope(ConfigurableFactory.SCOPE_PROTOTYPE)
public Notepad notepad() {
    return new Notepad();
}

&lt;bean id=&quot;notepad&quot; class=&quot;com.myapp.Notepad&quot; scope=&quot;prototype&quot;/&gt;
</code></pre>
<h3 id="使用会话和请求作用域"><a href="#使用会话和请求作用域" class="headerlink" title="使用会话和请求作用域"></a>使用会话和请求作用域</h3><ul>
<li><p>会话</p>
<pre><code class="Java">  @Component
  @Scope(
      value=WebApplicationContext.SCOPE_SESSION,
      proxyMode=ScopedProxyMode.INTERFACES)
  public ShoppingCart cart() { ... }
  // proxyMode解决了将会话或请求作用域的bean注入到单例bean中所遇到的问题。
  // 当StoreService调用ShoppingCart的方法时，代理会对其进行懒解析并调用给会话作用域内真正的ShoppingCart bean

  proxyMode=ScopedProxyMode.TARGET_CLASS
  // 表明要以生成目标类扩展的方式创建代理
</code></pre>
</li>
<li><p>作用域代理能够延迟注入请求和会话作用域的bean</p>
</li>
</ul>
<h3 id="在-XML-中声明作用域代理"><a href="#在-XML-中声明作用域代理" class="headerlink" title="在 XML 中声明作用域代理"></a>在 XML 中声明作用域代理</h3><pre><code class="Java">&lt;bean id=&quot;cart&quot; class=&quot;com.myapp.ShoppingCart&quot; scope=&quot;session&quot;&gt;
    &lt;aop:scoped-proxy /&gt;
&lt;/bean&gt;

&lt;aop:scoped-proxy proxy-target-class=&quot;false&quot; /&gt;
// 要求它生成基于接口的代理
</code></pre>
<h2 id="运行时值注入"><a href="#运行时值注入" class="headerlink" title="运行时值注入"></a>运行时值注入</h2><ul>
<li><p>Spring 提供了两种在运行时求值的方式：</p>
<ul>
<li>属性占位符(Property placeholder)</li>
<li>Spring表达式语言(SpEL)</li>
</ul>
</li>
</ul>
<h3 id="注入外部的值"><a href="#注入外部的值" class="headerlink" title="注入外部的值"></a>注入外部的值</h3><pre><code class="Java">@Configuration
@PropertySource(&quot;classpatch:/com/soundsystem/app/properties&quot;)
public class ExpressiveConfig {
    @Autowired
    Environment env;

    @Bean
    public BlankDisc disc() {
        return new BlankDisc(
            env.getProperty(&quot;disc.title&quot;),
            env.getProperty(&quot;disc.artist&quot;));
    }
}
</code></pre>
<h4 id="深入学习-Spring-的-Environment"><a href="#深入学习-Spring-的-Environment" class="headerlink" title="深入学习 Spring 的 Environment"></a>深入学习 Spring 的 Environment</h4><ul>
<li><p>getProperty()</p>
<ul>
<li>String getProperty(String key) // ，没有定义时，获取到的值是null</li>
<li>String getProperty(String key, String defaultValue)</li>
<li>T getProperty(String key, Class<t> type)</t></li>
<li>T getProperty(String key, Class<t> type, T defaultValue)<ul>
<li>int connectionCount = env.getProperty(“db.connection.count”, Integer.class, 30);</li>
</ul>
</t></li>
</ul>
</li>
<li><p>getRequiredProperty(“disc.title”) // 属性没有定义的话，会抛出IllegalStateException</p>
</li>
<li><p>boolean containsProperty()</p>
</li>
<li>getPropertyAsClass() // 将属性解析为类<ul>
<li>Class<compactdisc> cdClass = env.getPropertyAsClass(“disc.class”<br>, CompactDisc.class);</compactdisc></li>
</ul>
</li>
<li>String[] getActiveProfiles(); // 返回激活profile名称的数组</li>
<li>String[] getDefaultfiles(); // 返回默认profile名称的数组</li>
<li>boolean acceptsProfiles(String… profiles)</li>
</ul>
<h3 id="解析属性占位符"><a href="#解析属性占位符" class="headerlink" title="解析属性占位符"></a>解析属性占位符</h3><ul>
<li><p><code>${ ... }</code></p>
<pre><code class="xml">  &lt;bean id=&quot;sgtPeppers&quot; 
          class=&quot;soundsystem.BlankDisc&quot; 
          c:_title=&quot;${disc.title}&quot; 
          c:_artist=&quot;${disc.artist}&quot; /&gt;
</code></pre>
<ul>
<li><p>依赖于组件扫描和自动装配来创建和初始化应用组件</p>
<pre><code class="Java">  public BlackDisc(
          @Value(&quot;${disc.title}&quot;) String title,
          @Value(&quot;${disc.artist}&quot;) String artist) {
      this.title = title;
      this.artist = artist;
  }
</code></pre>
</li>
</ul>
</li>
<li><p>为了使用占位符，必须配置一个PropertySourcesPlaceholderConfigurer bean</p>
<pre><code class="Java">  @Bean
  public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
      return new PropertySourcePlaceholderConfigurer();
  }

  // XML
  &lt;context:property-placeholder /&gt;
</code></pre>
</li>
<li><p>解析外部属性能够将值的处理推迟到运行时，但是它的关注点在于根据名称解析来自于Spring Environment和属性源的属性。</p>
</li>
</ul>
<h3 id="使用-Spring-表达式语言进行装配"><a href="#使用-Spring-表达式语言进行装配" class="headerlink" title="使用 Spring 表达式语言进行装配"></a>使用 Spring 表达式语言进行装配</h3><ul>
<li><p>（Spring引入）Spring Expression Language (SpEL),特性：</p>
<ul>
<li>使用bean的ID来引用bean</li>
<li>调用方法和访问对象的属性</li>
<li>对值进行算术，关系和逻辑运算</li>
<li>正则表达式匹配</li>
<li>集合操作</li>
</ul>
</li>
<li><p>SpEL表达式要放到“#{ … }”之中</p>
<pre><code class="Java">  #{T(System).currentTimeMillis()
  // 计算机表达式的那一刻当前时间的毫秒数
  // T()表达式 会将java.lang.System视为Java中对应的类型

  // 引用其他的bean或其他bean的属性
  #{sgtPeppers.artist}

  #{systemProperties[&#39;disc.title&#39;]}
  // 引用系统属性

  public BlankDisc(
          @Value(&quot;#{systemProperty[&#39;disc.title&#39;]}&quot; String title,
          @Value(&quot;#{systemProperty[&#39;disc.artist&#39;]}&quot; String artist) {
      this.title = title;
      this.artist = artist;
  }

  &lt;bean id=&quot;sgtPeppers&quot; 
        class=&quot;soundsystem.BlankDisc&quot;
        c:_title=&quot;#{systemProperties[&#39;disc.title&#39;]}&quot;
        c:_artist=&quot;#{systemProperties[&#39;disc.artist&#39;]}&quot; /&gt;
</code></pre>
</li>
</ul>
<h4 id="表示字面值"><a href="#表示字面值" class="headerlink" title="表示字面值"></a>表示字面值</h4><h4 id="引用-bean，属性和方法"><a href="#引用-bean，属性和方法" class="headerlink" title="引用 bean，属性和方法"></a>引用 bean，属性和方法</h4><ul>
<li>SpEL所能做的另外一件基础的事情就是通过ID引用其他的bean。</li>
</ul>
<pre><code class="Java">#{artistSelector.selectArtist()?.toUpperCase()}
// 与使用“.”符号不同，&quot;?.&quot;可以在访问它右边内容之前，确保它对应的元素不是null
// 如果artistSelector.selectArtist()返回值时是null的话，那么SpEL将不会调用toUpperCase()方法。表达式返回值是null
</code></pre>
<h4 id="在表达式中使用类型"><a href="#在表达式中使用类型" class="headerlink" title="在表达式中使用类型"></a>在表达式中使用类型</h4><ul>
<li>在SpEL中访问类作用域的方法和常量的话，要依赖T()这个关键的运算符</li>
</ul>
<pre><code class="Java">T(java.lang.Math) // 结果会是一个Class对象
T(java.lang.Math).PI
T(java.lang.Math).random()
</code></pre>
<h4 id="SpEL-运算符"><a href="#SpEL-运算符" class="headerlink" title="SpEL 运算符"></a>SpEL 运算符</h4><pre><code class="Java">#{T(java.lang.math).PI * circle.radius ^ 2}
#{disc.title &#39; by &#39; + disc.artist}
#{counter.total == 100}
#{scoreboard.score &gt; 1000 ? &quot;Winner!&quot; : &quot;Loser&quot;}

// 三元表达式一个常见的场景就是检查null，并用一个默认值来代替null
#{disc.title ?: &#39;Rattle and Hum&#39;}
// 这种表达式通常称为Elvis运算符。
</code></pre>
<h4 id="计算正则表达式"><a href="#计算正则表达式" class="headerlink" title="计算正则表达式"></a>计算正则表达式</h4><pre><code class="Java">#{admin.email matches &#39;[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.com&#39;}
// 返回Boolean值
</code></pre>
<h4 id="计算集合"><a href="#计算集合" class="headerlink" title="计算集合"></a>计算集合</h4><pre><code class="Java">#{jukebox.songs[4].title}
#{&#39;This is a test&#39;[3]}

// 查询运算符（.?[]）,对集合进行过滤
#{jukebox.songs.?[artist eq &#39;Aerosmith&#39;]}

// .^[], .$[]  分别用来在集合中查询第一个匹配项和最后一个匹配项
#{jukebox.sons.^[artist eq &#39;Aerosmith&#39;]}

// 投影运算符 .![], 会从集合的每个成员中选择特定的属性放到另外一个集合中。
#{jukebox.songs.![title]}

#{jukebox.songs.?[artist eq &#39;Aerosmith&#39;].![title]}
</code></pre>
<h1 id="面向切面的-Spring"><a href="#面向切面的-Spring" class="headerlink" title="面向切面的 Spring"></a>面向切面的 Spring</h1><ul>
<li>软件开发中，散布于应用中多处的功能被称为横切关注点（cross-cutting concern）</li>
<li>AspectJ</li>
</ul>
<h2 id="什么是面向切面编程"><a href="#什么是面向切面编程" class="headerlink" title="什么是面向切面编程"></a>什么是面向切面编程</h2><h3 id="定义-AOP-术语"><a href="#定义-AOP-术语" class="headerlink" title="定义 AOP 术语"></a>定义 AOP 术语</h3><ul>
<li><p>通知（Advice）</p>
<ul>
<li><p>定义了切面是什么以及何时使用</p>
</li>
<li><p>Spring切面可以应用5种类型的通知：</p>
<ul>
<li>前置通知(Before)</li>
<li>后置通知(After)</li>
<li>返回通知(After-returning)</li>
<li>异常通知(After-throwing)</li>
<li>环绕通知(Around)</li>
</ul>
</li>
</ul>
</li>
<li><p>连接点(Join point)</p>
<ul>
<li>连接点是在应用执行过程中能够插入切面的一个点。这个可以是调用方法时，抛出异常时，甚至是修改一个字段时。切面代码可以利用这些插入到应用的正常流程之中，并添加到新的行为。</li>
</ul>
</li>
<li><p>切点(Poincut)</p>
<ul>
<li>通知定义了切面的“什么”和“何时”的话，那切点就定义了“何处”。</li>
<li>切点的定义会匹配通知所要织入的一个或多个切点。</li>
<li>我们通常使用明确的类和方法名称，或是利用正则表达式定义所匹配的类和方法来指定这些切点。</li>
</ul>
</li>
<li><p>切面(Aspect)</p>
<ul>
<li>切面是通知和切点的结合。</li>
</ul>
</li>
<li><p>引入(Introduction)</p>
<ul>
<li>引入允许我们向现有的类添加新方法或属性。</li>
</ul>
</li>
<li><p>织入(Weaving)</p>
<ul>
<li>织入是把切面应用到目标对象并创建新的代理对象的过程。</li>
<li>切面在指定的连接点被织入到对象目标对象中。</li>
<li>在目标对象的生命周期里有多个点可以进行织入：<ul>
<li>编译期 // 切面在目标类编译时被织入。 AspectJ</li>
<li>类加载期 // 切面在目标类加载到JVM时被织入。 AspectJ5</li>
<li>运行期 // 切面在应用运行的某个时刻被织入。在织入切面时，AOP容器会为目标对象动态地创建一个代理对象。 Spring AOP</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="Spring-对-AOP-的支持"><a href="#Spring-对-AOP-的支持" class="headerlink" title="Spring 对 AOP 的支持"></a>Spring 对 AOP 的支持</h3><ul>
<li><p>不同的AOP框架在连接点模型上可能有强弱之分</p>
</li>
<li><p>Spring提供了4种类型的AOP支持</p>
<ul>
<li>基于代理的经典Spring AOP</li>
<li>纯POJO切面</li>
<li>@AspectJ注解驱动的切面</li>
<li>注入式AspectJ切面(适用于Spring各版本)</li>
</ul>
</li>
<li><p>前三种都是Spring AOP实现的变体，Spring AOP构建在动态代理基础之上。Spring对AOP的支持局限于方法拦截</p>
</li>
</ul>
<h4 id="Spring-通知是-Java-编写的"><a href="#Spring-通知是-Java-编写的" class="headerlink" title="Spring 通知是 Java 编写的"></a>Spring 通知是 Java 编写的</h4><h4 id="Spring-在运行时通知对象"><a href="#Spring-在运行时通知对象" class="headerlink" title="Spring 在运行时通知对象"></a>Spring 在运行时通知对象</h4><ul>
<li>直到应用需要被代理的bean时，Spring才创建代理对象。</li>
</ul>
<h4 id="Spring-只支持方法级别的连接点"><a href="#Spring-只支持方法级别的连接点" class="headerlink" title="Spring 只支持方法级别的连接点"></a>Spring 只支持方法级别的连接点</h4><ul>
<li>因为Spring是基于动态代理的。</li>
</ul>
<h2 id="通过切点来选择连接点"><a href="#通过切点来选择连接点" class="headerlink" title="通过切点来选择连接点"></a>通过切点来选择连接点</h2><ul>
<li>Spring仅支持AspectJ切点指示器(pointcut designator)的一个子集。</li>
<li><p>Spring借助AspectJ的切点表达式语言来定义Spring切面。</p>
<ul>
<li>arg() 限制连接点匹配参数为指定类型的执行方法</li>
<li>@args() 限制连接点匹配参数由指定注解标注的执行方法</li>
<li>execution() 用于匹配是连接点的执行方法</li>
<li>this() 限制连接点匹配AOP代理的bean引用为指定类型的类</li>
<li>target 限制连接点匹配目标对象为指定类型的类</li>
<li>@target() 限制连接点匹配待定的执行对象，这些对象对应的类型要具有指定类型的注解</li>
<li>within() 限制连接点匹配指定的类型</li>
<li>@within() 限制连接点匹配指定注解所标注的类型(当使用Spring AOP时，方法定义在由指定的注解所标注的类型)</li>
<li>@annotation 限定匹配带有指定注解的连接点</li>
</ul>
</li>
<li><p>以上只有execution指示器是实际执行匹配，而其他指示器都是用来限制匹配的</p>
</li>
<li>execution指示器是我们在编写切点定义时最主要使用的指示器，在此基础上我们其他指示器阿里限制所匹配的切点</li>
</ul>
<h3 id="编写切点"><a href="#编写切点" class="headerlink" title="编写切点"></a>编写切点</h3><pre><code class="Java">execution(* concert.Performance.perform(..))

// execution 在方法执行时触发
// * 返回任意类型
// concert.Performance 方法所属的类
// perform 方法
// .. 指定任意参数，表明切点要选择任意的perform()方法，无论该方法的入参是什么

execution(* concert.Performance.perform(..)) &amp;&amp; within(concert.*)
// XML中使用and，not，or
</code></pre>
<h3 id="在切点中选择-bean"><a href="#在切点中选择-bean" class="headerlink" title="在切点中选择 bean"></a>在切点中选择 bean</h3><ul>
<li>Spring引入了一个新的bean()指示器，它允许我们在切点表达式中使用bean的ID来标识。</li>
</ul>
<pre><code class="Java">execution(* concert.Performance.perform() and bean(&#39;woodstock&#39;))
</code></pre>
<h2 id="使用注解创建切面"><a href="#使用注解创建切面" class="headerlink" title="使用注解创建切面"></a>使用注解创建切面</h2><ul>
<li>使用注解来创建切面是AspectJ5所引入的关键特性。</li>
</ul>
<h3 id="定义切面"><a href="#定义切面" class="headerlink" title="定义切面"></a>定义切面</h3><pre><code class="Java">@Aspect
public class Audience {
    @Before(&quot;execution(** concert.Performance.perform(..))&quot;)
    public void silenceCellPhones() {
        System.out.println(&quot;Silence cell phones&quot;);
    }

    @Before(&quot;execution(** concert.Performance.perform(..))&quot;)
    public void takeSeats() {
        System.out.println(&quot;Taking seats&quot;);
    }

    // 表演之后
    @AfterReturning(&quot;execution(** concert.Performance.perform(..))&quot;)
    public void applause() {
        System.out.println(&quot;CLAP CLAP CLAP!!!&quot;);
    }

    // 表演失败之后
    @AfterThrowing(&quot;execution(** concert.Performance.perform(..))&quot;)
    public void demandRefund() {
        System.out.println(&quot;Demanding a refund&quot;);
    }
}
</code></pre>
<ul>
<li><p>Spring使用AspectJ注解来声明通知方法</p>
<ul>
<li>@After 通知方法会在目标方法返回值或抛出异常后调用</li>
<li>@AfterReturning 通知方法会在目标方法返回后调用</li>
<li>@AfterThrowing 通知方法会在目标方法抛出异常后调用</li>
<li>@Around 通知方法会将目标方法封装起来</li>
<li>@Before 通知方法会在目标方法调用之前执行</li>
</ul>
</li>
<li><p><code>@Pointcut</code>注解能够在一个<code>@AspectJ</code>切面内定义可重用的切点。</p>
<pre><code class="java">  @Aspect
  public class Audience {
      @Pointcut(&quot;execution(** concert.Performance.perform(..))&quot;) // 定义命名的切点
      public void performance() {}

      @Before(&quot;performance()&quot;)
      public void silenceCellPhones() {
          System.out.println(&quot;Silence cell phones&quot;);
      }

      @Before(&quot;performance()&quot;)
      public void takeSeats() {
          System.out.println(&quot;Taking seats&quot;);
      }

      @AfterReturning(&quot;performance()&quot;)
      public void applause() {
          System.out.println(&quot;CLAP CLAP CLAP!!!&quot;);
      }

      @AfterThrowing(&quot;performance()&quot;)
      public void demandRefund() {
          System.out.println(&quot;Demanding a refund&quot;);
      }
  }
</code></pre>
</li>
<li><p>JavaConfig中启用AspectJ注解的自动代理</p>
<pre><code class="Java">  @Configuration
  @EnableAspectJAutoProxy // 启用AspectJ自动代理
  @ComponentScan
  public class ConcertConfig() {

      @Bean
      public Audience audience() { // 声明Audience bean
          return new Audience();
      }
  }
</code></pre>
</li>
<li><p>XML装配bean</p>
<pre><code class="xml">   &lt;context:component-scan base-package=&quot;concert&quot; /&gt;
   &lt;aop:aspectj-autoproxy /&gt;
   &lt;bean class=&quot;concert.Audience&quot; /&gt;
</code></pre>
</li>
</ul>
<h3 id="创建环绕通知"><a href="#创建环绕通知" class="headerlink" title="创建环绕通知"></a>创建环绕通知</h3>
 
                <!-- Meta -->
                <div class="post-meta">
                    <hr>
                    <br>
                    <div class="post-tags">
                        
                            

<a href="/tags/Spring-in-Action/">#Spring in Action</a>


                        
                    </div>
                    <div class="post-date">
                        2017 年 04 月 12 日
                    </div>
                </div>
            </div>

            <!-- Comments -->
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
                <!-- Disqus Comments -->


            </div>
        </div>
    </div>
</article>
</section>

    <!-- Scripts -->
    <!-- jQuery -->
<script src="//cdn.bootcss.com/jquery/2.2.1/jquery.min.js"></script>
<!-- Bootstrap -->
<script src="//cdn.bootcss.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>

<script type="text/javascript">
	hljs.initHighlightingOnLoad();
	console.log('Hexo-theme-hollow designed by zchen9 http://www.chen9.info © 2015-' + (new Date()).getFullYear());
</script>

    <!-- Google Analytics -->
    

</body>

</html>